using UnityEngine;
using DA.AnimGraph;
using System;
using System.Collections.Generic;
using System.Linq;

namespace DA.AnimGraph.Editor {
    /// <summary>
    /// 抽象基类
    /// </summary>
    internal abstract class AGAHandleBase {

        internal abstract AUID uuid { get; }

        internal readonly HandleManager handleManager;
        internal readonly CommandManager commandManager;

        internal uint version { get; private set; }
        internal bool isValid { get; private set; }
        internal AGAHandleBase parent { get; private set; }

        internal AGAHandleBase(AGAHandleBase parent, HandleManager handleManager, CommandManager commandManager) {
            this.parent = parent;
            this.handleManager = handleManager;
            this.commandManager = commandManager;
            version = 1;
            isValid = true;
        }

        internal void SetDirty() {
            version++;
            //OnUpdate?.Invoke();
            handleManager?.SetDirty();
        }

        internal void Destroy() {
            version++;
            isValid = false;
            //handleManager = null;
            //commandManager = null;
            parent = null;
            // data = null;
        }

        internal HandleKeeper<T> ToKeeper<T>() where T : AGAHandleBase {
            Debug.Assert(this is T);
            return new HandleKeeper<T>(this as T);
        }

        internal AGAHandleBase GetChildHandle(AUID childUuid) {
            var handle = handleManager?.GetHandle(childUuid);
            if (handle != null) {
                return handle;
            }
            handle = GetChildHandleInternal(childUuid);
            handleManager?.AddHandle(handle);

            return handle;
        }

        protected abstract AGAHandleBase GetChildHandleInternal(AUID childUuid);
    }

    /// <summary>
    /// 数据根handle
    /// </summary>
    internal class AGAHandle_Root : AGAHandleBase {
        private AGA_Root data;
        internal override AUID uuid => data.uuid;
        public int graphCount => data.graphs.Count;

        public AGAHandle_Root(AGA_Root data, HandleManager handleManager, CommandManager commandManager) : base(null, handleManager, commandManager) {
            this.data = data;
        }

        public AUID[] GetAllGraphsUuid() {
            var arr = data.graphs.Select((graph) => graph.uuid).ToArray();
            return arr;
        }

        /// <summary>
        /// 遍历graph
        /// </summary>
        /// <param name="callback"></param>
        public void ForeachGraphs(Action<AGAHandle_Graph> callback) {
            var graphUuids = GetAllGraphsUuid();
            foreach (var graphUuid in graphUuids) {
                var graphHandle = GetChildHandle(graphUuid) as AGAHandle_Graph;
                callback(graphHandle);
            }
        }

        public AGAHandle_Graph NewGraph(string name = null) {
            SetDirty();
            var graph = new AGA_Graph();
            data.graphs.Add(graph);
            var graphHandle = new AGAHandle_Graph(graph, this, handleManager, commandManager);
            graphHandle.name = name ?? "new graph";
            graphHandle.NewSubGraph();
            handleManager?.AddHandle(graphHandle);
            return graphHandle;
        }
        public bool DeleteGraph(AUID uuid) {
            if (data.graphs.Count == 1) {
                D.Exception("不允许删除最后一个graph");
                return false;
            }
            var index = data.FindGraph(uuid);
            if (index >= 0) {
                SetDirty();
                data.graphs.RemoveAt(index);
                handleManager?.RemoveHandle(uuid);
                return true;
            }
            return false;
        }
        public bool RenameGraph(AUID uuid, string name) {
            if (string.IsNullOrEmpty(name)) {
                return false;
            }
            var index = data.FindGraph(uuid);
            if (index >= 0) {
                if (name == data.graphs[index].name) {
                    return false;
                }
                // 应当让子级的handle处理
                var childHandle = GetChildHandle(uuid) as AGAHandle_Graph;
                childHandle.name = name;
                SetDirty();
                return true;
            }
            return false;
        }

        #region 黑板

        public List<AGA_Blackboard.Entry> blackboardEntries => data.blackboard.entries;

        public List<AGA_Blackboard.Entry> GetBlackboardEntriesInType(AGABlackboardType type) {
            return data.blackboard.entries.Where(x => x.type == type).ToList();
        }

        public AGA_Blackboard.Entry? GetBlackboardEntryByName(string name) {
            var hasName = data.blackboard.GetEntryByName(name, out AGA_Blackboard.Entry result);
            if (!hasName) {
                return null;
            }
            return result;
        }
        
        /// <summary>
        /// 传入名字的一部分就能搜索，不区分大小写
        /// </summary>
        public AGA_Blackboard.Entry[] GetBlackboardEntriesBySearch(string name) {
            var result = data.blackboard.GetEntriesBySearch(name);
            return result;
        }

        public Dictionary<string, AGABlackboardType> GetBlackboardInfo() {
            var result = new Dictionary<string, AGABlackboardType>(data.blackboard.entries.Count);
            foreach (var e in data.blackboard.entries) {
                result.Add(e.name, e.type);
            }
            return result;
        }

        public AUID AddBlackboardEntry(string name, AGABlackboardType type) {
            AUID uuid = AUIDGenerator.Gen();
            if (string.IsNullOrEmpty(name)) {
                name = GetNewBlackboardEntryName(uuid, "new variable");
            } else {
                name = GetNewBlackboardEntryName(uuid, name);
            }
            var entry = new AGA_Blackboard.Entry() {
                uuid = uuid,
                name = name,
                type = type,
            };
            data.blackboard.entries.Add(entry);
            SetDirty();
            return entry.uuid;
        }

        public void RemoveBlackboardEntry(AUID uuid) {
            var index = data.blackboard.FindEnry(uuid);
            if (index >= 0) {
                data.blackboard.entries.RemoveAt(index);
                SetDirty();
            }
        }

        public void ModifyBlackboardEntry(AUID uuid, AGA_Blackboard.Entry newEntry) {
            var index = data.blackboard.FindEnry(uuid);
            if (index >= 0) {
                newEntry.name = GetNewBlackboardEntryName(uuid, newEntry.name);
                data.blackboard.entries[index] = newEntry;
                SetDirty();
            }
        }

        /// <summary>
        /// 避免重名
        /// </summary>
        string GetNewBlackboardEntryName(AUID uuid, string name) {
            string newName = name;
            int number = 1;
            while (true) {
                bool needChangeName = false;
                foreach (var entry in blackboardEntries) {
                    if (entry.name == newName && uuid != entry.uuid) {
                        needChangeName = true;
                        break;
                    }
                }
                if (needChangeName) {
                    newName = $"{name}({number++})";
                } else {
                    break;
                }
            }
            return newName;
        }

        #endregion 黑板

        protected override AGAHandleBase GetChildHandleInternal(AUID childUuid) {
            var index = data.FindGraph(childUuid);
            if (index < 0) { return null; }
            var graphHandle = new AGAHandle_Graph(data.graphs[index], this, handleManager, commandManager);
            return graphHandle;
        }
    }

    /// <summary>
    /// 完整graph的handle
    /// </summary>
    internal class AGAHandle_Graph : AGAHandleBase {
        AGA_Graph data;

        internal override AUID uuid => data.uuid;

        internal AGAHandle_Graph(AGA_Graph graph, AGAHandleBase parent, HandleManager handleManager, CommandManager commandManager) : base(parent, handleManager, commandManager) {
            data = graph;
        }

        internal string name {
            get => data.name;
            set {
                data.name = value;
                SetDirty();
            }
        }

        internal AUID rootSubGraph => data.rootSubGraph;
        public AGAHandle_SubGraph rootSubGraphHandle => GetChildHandle(rootSubGraph) as AGAHandle_SubGraph;

        public List<AUID> GetAllSubGraphsUuid() {
            var list = data.subGraphs.Select((sub) => sub.uuid).ToList();
            return list;
        }

        public AGAHandle_SubGraph NewSubGraph() {
            SetDirty();
            var subGraph = new AGA_SubGraph();
            if (data.subGraphs.Count == 0) {
                data.rootSubGraph = subGraph.uuid;
            }
            data.subGraphs.Add(subGraph);
            var handle = new AGAHandle_SubGraph(subGraph, this, handleManager, commandManager);
            handle.name = "new sub graph";
            handle.AddResultNode();
            handleManager?.AddHandle(handle);
            return handle;
        }

        public bool DeleteSubGraph(AUID uuid) {
            var index = data.FindeSubGraph(uuid);
            if (index >= 0) {
                SetDirty();
                data.subGraphs.RemoveAt(index);
                handleManager?.RemoveHandle(uuid);
                return true;
            }
            return false;
        }

        protected override AGAHandleBase GetChildHandleInternal(AUID childUuid) {
            var index = data.FindeSubGraph(childUuid);
            if (index < 0) { return null; }
            var subGraphHandle = new AGAHandle_SubGraph(data.subGraphs[index], this, handleManager, commandManager);
            return subGraphHandle;
        }
    }

    /// <summary>
    /// subGraph的Handle
    /// </summary>
    internal class AGAHandle_SubGraph : AGAHandleBase {
        AGA_SubGraph data;
        public AUID subGraphUuid => data.uuid;

        internal override AUID uuid => data.uuid;

        internal AGAHandle_SubGraph(AGA_SubGraph subGraph, AGAHandleBase parent, HandleManager handleManager, CommandManager commandManager) : base(parent, handleManager, commandManager) {
            data = subGraph;
        }

        internal string name {
            get => data.name;
            set {
                data.name = value;
                SetDirty();
            }
        }

        public AUID[] GetAllNodesUuid() {
            var arr = data.nodes.Select(node => node.uuid).ToArray();
            return arr;
        }

        /// <summary>
        /// 根据nodeName查找节点 
        /// </summary>
        public AUID[] GetNodesUuidByName(string nodeName) {
            var arr = data.nodes.Where(node => {
                if (node is AGANode_FunctionBase fNode && fNode.nodeName == nodeName) {
                    return true;
                }
                return false;
            }).Select(node => node.uuid).ToArray();
            return arr;
        }

        public List<AGA_Edge> GetAllEdges() {
            return data.edges;
        }

        /// <summary>
        /// 遍历所有节点
        /// </summary>
        /// <param name="callback">返回是否dirty，即是否修改了内容</param>
        internal void ForeachNodes(Action<AGAHandle_Node> callback) {
            var nodeUuids = GetAllNodesUuid();
            foreach (var nodeUuid in nodeUuids) {
                var nodeHandle = GetChildHandle(nodeUuid) as AGAHandle_Node;
                callback(nodeHandle);
            }
        }

        public AGAHandle_Node AddResultNode() {
            SetDirty();
            AGANode_Result resultNode = new AGANode_Result();
            resultNode.position = new Vector2(500, 300);
            data.rootNode = resultNode.uuid;
            data.nodes.Add(resultNode);
            var handle = new AGAHandle_Node(resultNode, this, handleManager, commandManager);
            handleManager?.AddHandle(handle);
            return handle;
        }

        // todo:应当返回handle 或 void
        public AGANode_FunctionBase AddNode(AGANodeModelBase nodeModel, Type bNodeType, Vector2 position) {
            SetDirty();
            //var runtimeNodeType = nodeModel.GetType().DeclaringType;
            //var bridgeNodeType = BridgeNodeMetaInfoRegister.GetBridgeNodeTypeByRuntimeNodeType(runtimeNodeType);
            //if (bridgeNodeType == null) { return default; }
            var node = Activator.CreateInstance(bNodeType, nodeModel) as AGANode_FunctionBase;
            if (node == null) {
                D.Exception($"无法实例化类型或类型不符合期望： {bNodeType}");
            }
            // 初始化数据
            node.position = position;

            data.nodes.Add(node);
            return node;
        }

        /// <summary>
        /// 直接添加，不通过反射执行构造函数。
        /// 保证uuid重新生成后再传入，不要重复了
        /// 粘贴的时候使用
        /// </summary>
        public void PasteNode(AGANode_NodeBase node) {
            data.nodes.Add(node);
            SetDirty();
        }

        public bool RemoveNode(AUID nodeUuid) {
            var index = data.FindNode(nodeUuid);
            if (index >= 0) {
                data.nodes.RemoveAt(index);
                handleManager?.RemoveHandle(nodeUuid);
                SetDirty();
                return true;
            }
            return false;
        }

        public AUID[] GetNodeResource(AUID nodeUuid) {
            var index = data.FindNode(nodeUuid);
            if (index >= 0) {
                if (data.nodes[index] is IResourceNode node) {
                    var resList = node.GetResources();
                    return resList;
                }
            }
            return null;
        }
        public void ForeachPerksOnNode(AUID nodeUuid, Action<AGAPerk_PerkBase> callback) {
            var index = data.FindNode(nodeUuid);
            if (index >= 0) {
                if (data.nodes[index] is AGANode_FunctionBase node && node.perks != null) {
                    for (int i = 0; i < node.perks.Count; i++) {
                        callback(node.perks[i]);
                    }
                }
            }
        }

        public bool ConnectNode(AGA_Edge edge) {
            // todo:检查输入输出是否反了（左端是输出，右端是输入）
            int nodeFIdx = data.FindNode(edge.inputNode);
            int nodeTIdx = data.FindNode(edge.outputNode);
            // todo:检查端口是否存在
            if (nodeFIdx >= 0 && nodeTIdx >= 0) {
                data.edges.Add(edge);
                SetDirty();
                return true;
            }
            return false;
        }

        public bool ConnectNode(AUID inputNode, AUID inputPort, AUID outputNode, AUID outputPort) {
            return ConnectNode(new AGA_Edge(inputNode, inputPort, outputNode, outputPort));
        }

        public bool DisconnectNode(AUID inputNode, AUID inputPort, AUID outputNode, AUID outputPort) {
            var index = data.FindEdge(inputNode, inputPort, outputNode, outputPort);
            if (index >= 0) {
                SetDirty();
                data.edges.RemoveAt(index);
                return true;
            }
            return false;
        }

        protected override AGAHandleBase GetChildHandleInternal(AUID childUuid) {
            var index = data.FindNode(childUuid);
            if (index < 0) { return null; }
            var node = data.nodes[index];

            var handle = Activator.CreateInstance(typeof(AGAHandle_Node), node, this, handleManager, commandManager) as AGAHandleBase;
            // todo:节点的handle是否可以取消具体类型
            //var handleType = BridgeDataHandleRegister.GetHandleTypeByBridgeNodeType(node.GetType());
            //Debug.Assert(handleType != null, "handleType is null. Check NodeDataHandleMetaInfo");
            //var handle = Activator.CreateInstance(
            //    handleType,
            //    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
            //    null,
            //    new object[] { node, this },
            //    null
            //) as BridgeDataHandleBase;
            return handle;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    internal class AGAHandle_Node : AGAHandleBase {
        public AGANode_NodeBase data;

        internal override AUID uuid => data.uuid;

        //public abstract Type viewNodeType { get; }
        public readonly Type agaNodeType;

        internal Vector2 position {
            get { return data.position; }
            set {
                if (data.position != value) {
                    data.position = value;
                    SetDirty();
                }
            }
        }

        public AGAHandle_Node(AGANode_NodeBase node, AGAHandleBase parent, HandleManager handleManager, CommandManager commandManager) : base(parent, handleManager, commandManager) {
            data = node;
            agaNodeType = node.GetType();
        }

        /// <summary>
        /// 用于保存节点修改数据
        /// 值类型需要赋值，或是需要转换格式的数据
        /// </summary>
        internal void ModifyModel(Action<AGANodeModelBase> modifyAction) {
            if (this.data is AGANode_FunctionBase fNode) {
                modifyAction(fNode.modelProperty);
                SetDirty();
            }
        }

        public string GetNodeName() {
            if (data is AGANode_FunctionBase fNode) {
                return fNode.nodeName;
            }
            return null;
        }

        #region input

        internal void AddInput(AUID uuid, string name) {
            if (this.data is AGANode_BlendBase fNode) {
                fNode.inputChildren.Add(uuid);
                fNode.inputNames.Add(name);
                SetDirty();
            }
        }

        internal void RemoveInput(AUID uuid) {
            if (this.data is AGANode_BlendBase fNode) {
                var index = fNode.inputChildren.IndexOf(uuid);
                if (index >= 0) {
                    fNode.inputChildren.RemoveAt(index);
                    fNode.inputNames.RemoveAt(index);
                    SetDirty();
                }
            }
        }

        internal void RenameInput(AUID uuid, string name) {
            if (this.data is AGANode_BlendBase fNode) {
                var index = fNode.inputChildren.IndexOf(uuid);
                if (index >= 0) {
                    fNode.inputNames[index] = name;
                    SetDirty();
                }
            }
        }

        internal void WriteAllInput(List<(AUID, string)> values) {
            if (this.data is AGANode_BlendBase fNode) {
                fNode.inputChildren.Clear();
                fNode.inputNames.Clear();
                for (int i = 0; i < values.Count; i++) {
                    fNode.inputChildren.Add(values[i].Item1);
                    fNode.inputNames.Add(values[i].Item2);
                }
                SetDirty();
            }
        }

        internal List<(AUID, string)> GetAllInput() {
            if (this.data is AGANode_BlendBase fNode) {
                var list = new List<(AUID, string)>(fNode.inputChildren.Count);
                for (int i = 0; i < fNode.inputChildren.Count; i++) {
                    list.Add((fNode.inputChildren[i], fNode.inputNames[i]));
                }
                return list;
            }
            return null;
        }

        #endregion

        #region perk

        int FindPerkByType(Type perkType) {
            if (this.data is AGANode_FunctionBase fNode) {
                if (fNode.perks == null) return -1;
                return fNode.perks.FindIndex(x => x.GetType() == perkType);
            }
            return -1;
        }

        int FindPerkByUuid(AUID uuid) {
            if (this.data is AGANode_FunctionBase fNode) {
                if (fNode.perks == null) return -1;
                return fNode.perks.FindIndex(x => x.uuid == uuid);
            }
            return -1;
        }

        /// <summary>
        /// 返回是否添加成功
        /// </summary>
        internal bool AddPerk(AGAPerkModelBase perkModel, Type perkType) {
            if (this.data is AGANode_FunctionBase fNode) {
                if (fNode.perks == null) {
                    fNode.perks = new List<AGAPerk_PerkBase>();
                } else {
                    // 若已包含则不添加，即不允许同类型perk同时存在
                    var index = FindPerkByType(perkType);
                    if (index >= 0) {
                        D.W($"当前已存在该类型perk： {perkType}");
                        return false;
                    }
                }
                var obj = Activator.CreateInstance(perkType, perkModel) as AGAPerk_PerkBase;
                fNode.perks.Add(obj);
                SetDirty();
                return true;
            }
            return false;
        }

        /// <summary>
        /// 是否含有某类型的perk
        /// </summary>
        internal bool TryRemovePerkByType(Type perkType) {
            if (this.data is AGANode_FunctionBase fNode) {
                var index = FindPerkByType(perkType);
                if (index >= 0) {
                    fNode.perks.RemoveAt(index);
                    if (fNode.perks.Count == 0) {
                        fNode.perks = null;
                    }
                    SetDirty();
                    return true;
                }
            }
            return false;
        }

        internal bool TryRemovePerkByUuid(AUID uuid) {
            if (this.data is AGANode_FunctionBase fNode) {
                var index = FindPerkByUuid(uuid);
                if (index >= 0) {
                    fNode.perks.RemoveAt(index);
                    if (fNode.perks.Count == 0) {
                        fNode.perks = null;
                    }
                    SetDirty();
                    return true;
                }
            }
            return false;
        }

        internal List<AGAPerk_PerkBase> GetPerks() {
            if (this.data is AGANode_FunctionBase fNode) {
                return fNode.perks;
            }
            return null;
        }

        #endregion

        protected override AGAHandleBase GetChildHandleInternal(AUID childUuid) {
            return null;
        }
    }
}